package org.yunai.protobuf.io;

import org.yunai.protobuf.conf.MessageConfig;
import org.yunai.protobuf.conf.ProjectConfig;
import org.yunai.protobuf.conf.StructConfig;
import org.yunai.protobuf.v1.Utils;

import java.io.*;

/**
 * 代码生成器
 * User: yunai
 * Date: 13-3-13
 * Time: 上午11:34
 */
public class CodeGenerator {

    /**
     *
     */
    public static void execute() throws IOException {
        for (ProjectConfig.Side side : ProjectConfig.sides) {
            executeSide(side);
        }
    }

    private static void executeSide(ProjectConfig.Side side) throws IOException {
        String[] packagePaths = makePackageFile(side);

        // 生成Message
        for (MessageConfig.Message message : MessageConfig.messages) {
            generateMessage(side, message, packagePaths[0]);

        }

        // 生成Struct
        for (StructConfig.Struct struct : StructConfig.structs) {
            generateStruct(side, struct, packagePaths[1]);
        }

        // core包处理
//        generateCorePackage(side, packagePaths[2]);
        generateCorePackage(side, packagePaths[2]);
    }

    private static void generateMessage(ProjectConfig.Side side, MessageConfig.Message message, String packagePath) throws IOException {
        // 创建文件
        String javaPath = packagePath + "/" + message.getPackPrefix() + "/" + message.getClassName() + ".java";
        File javaFile = new File(javaPath);
        javaFile.createNewFile();

        // 写文件
        String pack = side.getPack() + ".message." + message.getPackPrefix();
        OutputStream os = null;
        BufferedWriter writer = null;
        try {
            os = new FileOutputStream(javaFile);
            writer = new BufferedWriter(new OutputStreamWriter(os));
            writer.write(CodeTemplate.formatClass(side, message, pack));
        } finally {
            Utils.closeIo(writer, os);
        }
    }

    private static void generateStruct(ProjectConfig.Side side, StructConfig.Struct struct, String packagePath) throws IOException {
        // 创建文件
        String javaPath = packagePath + "/" + struct.getName() + ".java";
        File javaFile = new File(javaPath);
        javaFile.createNewFile();

        // 写文件
        String pack = side.getPack() + ".struct";
        OutputStream os = null;
        BufferedWriter writer = null;
        try {
            os = new FileOutputStream(javaFile);
            writer = new BufferedWriter(new OutputStreamWriter(os));
            writer.write(CodeTemplate.formatClass(side, struct, pack));
        } finally {
            Utils.closeIo(writer, os);
        }
    }

    /**
     * 生成core包的类代码
     */
    private static void generateCorePackage(ProjectConfig.Side side, String packagePath) throws IOException {
//        String[] clazzs = new String[] {
//                AbstractDecoder.class.getSimpleName(),
//                AbstractEncoder.class.getSimpleName(),
//                "Bits",
//                ByteArray.class.getSimpleName(),
//                org.yunai.protobuf.base.Message.class.getSimpleName(),
//                org.yunai.protobuf.base.Struct.class.getSimpleName()
//        };
//        String[] clazzs = new String[] {
//                "AbstractDecoder",
//                "AbstractEncoder",
//                "Bits",
//                "ByteArray",
//                "Message",
//                "Struct"
//        };
//
//        for (String clazz : clazzs) {
//            InputStream is = null;
//            BufferedReader br = null;
//            OutputStream os = null;
//            BufferedWriter writer = null;
//
//            // 创建文件
//            String javaPath = packagePath + "/" + clazz + ".java";
//            File javaFile = new File(javaPath);
//            javaFile.createNewFile();
//
//            try {
//                is = Thread.currentThread().getContextClassLoader().getResourceAsStream("core/" + clazz + ".txt");
//                os = new FileOutputStream(javaFile);
//                writer = new BufferedWriter(new OutputStreamWriter(os));
//
//                br = new BufferedReader(new InputStreamReader(is));
//                int readLineCount = 0;
//                String line;
//                while ((line = br.readLine()) != null) {
//                    if (readLineCount > 0) {
//                        writer.write(line);
//                    } else {
//                        writer.write("package " + side.getPack() + ".core;");
//                    }
//                    writer.newLine();
//                    readLineCount ++;
//                }
//                writer.flush();
//            } finally {
//                Utils.closeIo(br, is, writer, os);
//            }
//        }

        // MessageManager生成
        String messageManager = "MessageManager";
        String javaPath = packagePath + "/" + messageManager + ".java";
        File javaFile = new File(javaPath);
        javaFile.createNewFile();
        InputStream is = null;
        BufferedReader br = null;
        OutputStream os = null;
        BufferedWriter writer = null;
        try {
            is = Thread.currentThread().getContextClassLoader().getResourceAsStream("core/" + messageManager + ".txt");
            os = new FileOutputStream(javaFile);
            writer = new BufferedWriter(new OutputStreamWriter(os));

            br = new BufferedReader(new InputStreamReader(is));
            int readLineCount = 0;
            String line;
            while ((line = br.readLine()) != null) {
                if (readLineCount > 0) {
                    if (readLineCount == 2) {
                        for (int i = 0, len = ProjectConfig.sides.size(); i < len; i++) {
                            for (int j = i + 1; j < len; j++) {
                                ProjectConfig.Side side1 = ProjectConfig.sides.get(i);
                                ProjectConfig.Side side2 = ProjectConfig.sides.get(j);
                                writer.write("import " + side.getPack() + ".message." + side1.getName() + "_" + side2.getName() + ".*;");
                                writer.newLine();
                                writer.write("import " + side.getPack() + ".message." + side2.getName() + "_" + side1.getName() + ".*;");
                                writer.newLine();
                            }
                        }
                    }
                    if (line.trim().equals("/** init **/")) {
                        for (MessageConfig.Message message : MessageConfig.messages) {
                            writer.write(CodeTemplate.LINE_SPACE_2 + "decoderMap.put(" + message.getClassName() + ".CODE, " + message.getClassName() + ".Decoder.getInstance());");
                            writer.newLine();
                            writer.write(CodeTemplate.LINE_SPACE_2 + "encoderMap.put(" + message.getClassName() + ".CODE, " + message.getClassName() + ".Encoder.getInstance());");
                            writer.newLine();
                        }
                    } else {
                        writer.write(line);
                    }
                } else {
                    writer.write("package " + side.getPack() + ";");
                }
                writer.newLine();
                readLineCount ++;
            }
            writer.flush();
        } finally {
            Utils.closeIo(br, is, writer, os);
        }
    }

    /**
     * 创建包路径文件
     * [0] 消息包
     * [1] 结构体包
     * [2] 解码包
     * [3] 编码包
     * @param side 端
     * @return 包路径
     */
    private static String[] makePackageFile(ProjectConfig.Side side) {
        String packagePath = side.getSrcPath() + (side.getSrcPath().endsWith("/") ? "" : "/")
                + side.getPack().replace(".", "/");
        String[] paths = new String[] {
                packagePath + "/message",
                packagePath + "/struct"
//                packagePath + "/core"
                , packagePath + "" // 取代下packagePath + "/core"
        };
        for (String path : paths) {
            File packageFile = new File(path);
            if (!packageFile.exists()) {
                packageFile.mkdirs();
            }
        }
        for (int i = 0, len = ProjectConfig.sides.size(); i < len; i++) {
            for (int j = i + 1; j < len; j++) {
                ProjectConfig.Side side1 = ProjectConfig.sides.get(i);
                ProjectConfig.Side side2 = ProjectConfig.sides.get(j);
                File packageFile = new File(paths[0] + "/" + side1.getName() + "_" + side2.getName());
                if (!packageFile.exists()) {
                    packageFile.mkdirs();
                }
                packageFile = new File(paths[0] + "/" + side2.getName() + "_" + side1.getName());
                if (!packageFile.exists()) {
                    packageFile.mkdirs();
                }
            }
        }
        return paths;
    }

}
